﻿/*****************************************************************************\
 *             University of Illinois/NCSA Open Source License
 *
 * 
 *      Copyright © 2012,2013, The Board of Trustees of the University of Illinois. All rights reserved.
 *
 *      Developed by: Rakesh Bobba, Derek Dagit, Erich Heine, Denis Kholine,
 *                    Klara Nahrstedt, David Rodgers,   William H. Sanders, Tim Yardley
 *
 *      Information Trust Institute/Coordinated Science Laboratory
 * 
 *      University of Illinois
 *      
 *      The Board of Trustees of the University of Illinois
 *      109 Coble Hall
 *      801 South Wright Street
 *      Champaign, IL 61820-6242
 *
 *      http://www.iti.illinois.edu / http://csl.illinois.edu
 * 
 *      Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 
 *      associated documentation files (the “Software”), to deal with the Software without restriction, 
 *      including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *      and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
 *      subject to the following conditions:
 *
 *      Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *      following disclaimers.
 *      
 *      Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
 *      the following disclaimers in the documentation and/or other materials provided with the distribution.
 *
 *      Neither the names of Information Trust institute, Coordinated Science Laboratory, University of Illinois,
 *      The Board of Trustees of the University of Illinois nor the names of its contributors may be used to 
 *      endorse or promote products derived from this Software without specific prior written permission.
 *      
 *      THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
 *      LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN
 *      NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *      WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
 *      SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 *      
 \*****************************************************************************/
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text.RegularExpressions;
using GSF;
using SIEGate.Core.Legacy;

namespace SIEGate.Core.Configurator
{
    /// <summary>
    /// A Config handler for turning the current GSF configuration stuff (aka
    /// ConnectionString and DataSet of everything in the database) into
    /// what configurator needs to build the graph, which is a list of 
    /// ConnectionInfo objects
    /// 
    /// TODO: make this not suck by:
    ///   * making a proper interface for it
    ///   * making a base class if it makes sense
    ///   * naming things better
    ///   * decide if we really really  want statics or what
    /// 
    /// </summary>
    public static class GsfHandler
    {
        private static readonly Regex s_filterExpression = new Regex("(FILTER[ ]+(TOP[ ]+(?<MaxRows>\\d+)[ ]+)?(?<TableName>\\w+)[ ]+WHERE[ ]+(?<Expression>.+)[ ]+ORDER[ ]+BY[ ]+(?<SortField>\\w+))|(FILTER[ ]+(TOP[ ]+(?<MaxRows>\\d+)[ ]+)?(?<TableName>\\w+)[ ]+WHERE[ ]+(?<Expression>.+))", RegexOptions.Compiled | RegexOptions.IgnoreCase);

        public static Dictionary<string, string> ParseConnectionString(string inputConnectionString)
        {
            Dictionary<string, string> result;

            // Preparse settings upon connection string assignment
            result = string.IsNullOrWhiteSpace(inputConnectionString) 
                ? new Dictionary<string, string>() : inputConnectionString.ParseKeyValuePairs();

            return result;
        }

        /// <summary>
        /// Parses input measurement keys from connection string setting.
        /// </summary>
        /// <param name="dataSource">The <see cref="DataSet"/> used to define input measurement keys.</param>
        /// <param name="value">Value of setting used to define input measurement keys, typically "inputMeasurementKeys".</param>
        /// <param name="measurementTable">Measurement table name used to load additional meta-data; this is not used when specifying a FILTER expression.</param>
        /// <returns>User selected stream IDs.</returns>
        public static HashSet<Guid> ParseStreamIDs(DataSet dataSource, string value, string measurementTable = "ActiveMeasurements")
        {
            bool dataSourceAvailable = ((object)dataSource != null);
            HashSet<Guid> streamIDs = new HashSet<Guid>();
            Match filterMatch = null;

            MeasurementKey key;
            Guid id;

            value = value.Trim();

            if (!string.IsNullOrWhiteSpace(value))
            {
                if (dataSourceAvailable)
                {
                    lock (s_filterExpression)
                    {
                        filterMatch = s_filterExpression.Match(value);
                    }
                }

                if (dataSourceAvailable && filterMatch.Success)
                {
                    string tableName = filterMatch.Result("${TableName}").Trim();
                    string expression = filterMatch.Result("${Expression}").Trim();
                    string sortField = filterMatch.Result("${SortField}").Trim();
                    string maxRows = filterMatch.Result("${MaxRows}").Trim();
                    int takeCount;

                    if (string.IsNullOrEmpty(maxRows) || !int.TryParse(maxRows, out takeCount))
                        takeCount = int.MaxValue;

                    foreach (DataRow row in dataSource.Tables[tableName].Select(expression, sortField).Take(takeCount))
                    {
                        if (Guid.TryParse(row["SignalID"].ToNonNullString(Guid.Empty.ToString()), out id))
                            streamIDs.Add(id);
                    }
                }
                else
                {
                    // Add manually defined measurement keys
                    foreach (string item in value.Split(';').Where(item => !string.IsNullOrWhiteSpace(item)))
                    {
                        if (MeasurementKey.TryParse(item, Guid.Empty, out key))
                        {
                            // Attempt to update empty signal ID if available
                            if (dataSourceAvailable && key.SignalID == Guid.Empty)
                            {
                                if (dataSource.Tables.Contains(measurementTable))
                                {
                                    DataRow[] filteredRows = dataSource.Tables[measurementTable].Select(string.Format("ID = '{0}'", key.ToString()));

                                    if (filteredRows.Length > 0)
                                        key.SignalID = filteredRows[0]["SignalID"].ToNonNullString(Guid.Empty.ToString()).ConvertToType<Guid>();
                                }
                            }

                            streamIDs.Add(key.SignalID);
                        }
                        else if (Guid.TryParse(item, out id))
                        {
                            streamIDs.Add(id);
                        }
                        else if (dataSourceAvailable && dataSource.Tables.Contains(measurementTable))
                        {
                            DataRow[] filteredRows = dataSource.Tables[measurementTable].Select(string.Format("PointTag = '{0}'", item));

                            if (filteredRows.Length > 0)
                            {
                                if (Guid.TryParse(filteredRows[0]["SignalID"].ToNonNullString(Guid.Empty.ToString()), out id))
                                    streamIDs.Add(id);
                            }
                        }
                    }
                }
            }

            return streamIDs;
        }
    }
}
